'''

Copyright 2010 Manuel Reinelt

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Created on 12.04.2010

@author: Manuel Reinelt
contact: mrdevlab@googlemail.com

'''

from Crypto.Cipher import Blowfish
from Crypto.Cipher import AES
import sys
import os

def crypt_data(cipher,key,datatodecenc,encdec,verbose):
    
    '''
    encrypt arbitrary data as blowfish or aes
    either with given or random key
    
    takes cipher, key, data to enc/decrypt
    encdec (string) and verbose
    gives encrypted data or exits with -1 on error
    '''
    
    def enc(data):
        return myCipher.encrypt(data)
    
    def dec(data):
        return myCipher.decrypt(data)
    
    if verbose:
        print "creating " + cipher + " cipher"
        sys.stdout.flush()  
        print encdec + "oding " + str(len(datatodecenc)) + " bytes"
        sys.stdout.flush()  
    
    if cipher == "blowfish":
        myCipher = Blowfish.new(key)
        blocksize = 8
    elif cipher == "aes":
        myCipher = AES.new(key)      
        blocksize = 16
    
    startpos = 0
    encdecbytes = ""
    bytestoencdec = ""
    progress = 0
        
    #iterate through databuffer:
    for i in range(len(datatodecenc)):
        
        progress = 100.0000000000 / len(datatodecenc) * i
        if progress % 5 == 0:
            #give 5 for reaching 100 at the end:
            progress += 5
            print "byte encoding-progress: %" + str(progress) + "%"
            sys.stdout.flush()
                                 
        #get block of bytes (8 for blowfish, 16 for aes) from buffer 
        #by checking mod of blocksize:
        if (i + 1) % blocksize == 0 and i <> 0:
            
            #bytestodecenc = one block of bytes do de/encode:
            bytestoencdec = datatodecenc[startpos:i + 1] 
            #encode or decode the block:
            if encdec == "enc":
                encdecbytes += enc(bytestoencdec)
            else:
                encdecbytes += dec(bytestoencdec)
            #move positionpointer to next block:
            startpos += blocksize
                
    #if there are bytes left < blocksize while encoding: 
    #add bytes to get a full block (pad)
    if len(datatodecenc) % blocksize > 0 and encdec == "enc":
    
        #calculate number of bytes to add to the "right side" to get a whole block:
        padright = blocksize - (len(datatodecenc) % blocksize) - 1
        if verbose:
            print "padding " + str(padright) + " bytes + final"     
        
        #create an amount of padbytes - 1 random bytes
        #(the last byte will be created to tell the decoder how many bytes
        #where padded while encoding):
       
        datatodecenc += os.urandom(padright)
        #datatodecenc += os.urandom(padright - 1)
            
        if verbose:
            print "creating random padbyte"
            sys.stdout.flush()  
        #THE padbtye (last byte to write) is random and calculated
        #to tell the the decoder the number of pad bytes. the decoder
        #mods blocksize on padbytes and receives the value:        
        padbyte = os.urandom(1)
        padbyte = ord(padbyte)
        padbyte -= (padbyte % blocksize) - padright
        #add the padbyte to the datatodecenc:
        datatodecenc += chr(padbyte)
        #create the final block in size of blocksize:
        bytestoencdec = datatodecenc[startpos:len(datatodecenc)]
        #encode the last block:
        encdecbytes = encdecbytes + enc(bytestoencdec)
    #on decoding we assume we have a full block of size blocksize
    #and read the last padbyte to receive how many padbytes we have
    #to delete from the "right side" of the last block, if it
    #differs from the blocksize:
    elif len(datatodecenc) <> blocksize * 1000000 and encdec == "dec":
        delright = ord(encdecbytes[-1]) % blocksize
        #plus the padbyte itself, if padded:
        if delright > 0:
            delright += 1
        #if bytes where padded, decode amount of padded bytes
        if delright:
            encdecbytes = encdecbytes[:-delright]
 
    if verbose:
        print encdec + "oded " + str(len(encdecbytes)) + " bytes"
        sys.stdout.flush()  
    
    return encdecbytes
            
